
package org.jeecg.modules.xindian.reconciliation.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.util.StringUtil;
import org.jeecg.modules.xindian.reconciliation.entity.*;
import org.jeecg.modules.xindian.reconciliation.enums.*;
import org.jeecg.modules.xindian.reconciliation.mapper.ChannelSettlementInfoMapper;
import org.jeecg.modules.xindian.reconciliation.mapper.PolicySettlementTypeStatisticsMapper;
import org.jeecg.modules.xindian.reconciliation.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static org.jeecg.modules.xindian.reconciliation.enums.RelationType.INSURANCE;


/**
 * @Description: 渠道结算汇总表
 * @Author: jeecg-boot
 * @Date: 2024-04-11
 * @Version: V1.0
 */
@Service
@Slf4j
public class ChannelSettlementInfoServiceImpl extends ServiceImpl<ChannelSettlementInfoMapper, ChannelSettlementInfo> implements IChannelSettlementInfoService {
    public static final String ALL = "ALL";
    @Autowired
    private IChannelSettlementConfigService configService;
    @Autowired
    private IInsuranceStatisticsService statisticsService;
    @Autowired
    private IInsuranceStatisticsService insuranceStatisticsService;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private IPolicySettlementTypeStatisticsService policySettlementTypeStatisticsService;
    @Autowired
    private PolicySettlementTypeStatisticsMapper policySettlementTypeStatisticsMapper;

    @Override
    public void synchronizeLastMonth() {
        //同步上个月数据
//        Date lastMounth = DateUtil.parseDate("2024-03-02");
        Date lastMounth = DateUtil.offsetMonth(new Date(), -1);
        synchronizeOneMonth(lastMounth);
    }

    @Override
    public void synchronizeOneMonth(Date date) {
        String lastMounthString = DateUtil.format(date, "yyyy-MM");
        //同步上个月的状态
        List<ChannelSettlementConfig> insuranceSettlementConfigs = configService.queryEnableConfig();
        //构建主记录
        List<ChannelSettlementInfo> addSettlementInfo = new ArrayList<>();
        List<PolicySettlementTypeStatistics> addSettlementPolicyRelation = new ArrayList<>();
        insuranceSettlementConfigs.forEach(cof -> {
            BigDecimal invoiceRate = cof.getInvoiceRate();
            BigDecimal fixInvoiceRate = BigDecimal.ONE.add(invoiceRate);
            cof.buildConfigBean();
            ChannelSettlementInfo info = new ChannelSettlementInfo();
            info.setId(IdUtil.fastUUID());
            info.setBenefit(BigDecimal.ZERO);
            info.setChannel(cof.getChannel());
            info.setDate(lastMounthString);
            info.setInsuranceAmt(BigDecimal.ZERO);
            info.setInsuranceNum(0);
            info.setState(SettlementState.CHECKING);
            List<PolicySettlementTypeStatistics> addPolicy = new ArrayList<>();
            Long numbers = lambdaQuery().eq(ChannelSettlementInfo::getChannel, cof.getChannel()).eq(ChannelSettlementInfo::getDate, lastMounthString).count();
            Assert.isTrue(numbers == 0, "渠道{}账单{}已被初始化过,不在重复加载", cof.getChannel(), lastMounthString);
            //对数据进行分组处理
            List<ChannelSettlementConfig.Config> configs = cof.getConfigs();
            Map<String, List<ChannelSettlementConfig.Config>> insuranceCompanyAreaGroup = configs.stream()
                    .collect(Collectors.groupingBy(ChannelSettlementConfig.Config::getInsuranceCompany));

            insuranceCompanyAreaGroup.keySet().forEach(insurance -> {

                List<ChannelSettlementConfig.Config> plantCf = insuranceCompanyAreaGroup.get(insurance);
                plantCf.forEach(cf -> {
                    List<InsuranceStatistics> insuranceStatistics = new ArrayList<>();
                    List<PolicySettlementTypeStatistics> relations = new ArrayList<>();
                    //判断是否有其他渠道的比例
                    if (cf.getType().equals(ChannelSettlementConfigType.OTHER)) {
                        insuranceStatistics = statisticsService.loadByInsuranceAreaAndPlatform(insurance, cf.getPlatform(), null, date);
                        relations = insuranceStatistics.stream().map(static1 -> {
                            BigDecimal rate = cf.getOtherPolicyRate();
                            PolicySettlementTypeStatistics policySettlementStatistics = policySettlementTypeStatisticsService.build(static1, info.getId(), RelationType.CHANNEL, rate);
                            policySettlementStatistics.setDate(lastMounthString);
                            policySettlementStatistics.setChannelAloneFee(policySettlementStatistics.getExTaxPremium().divide(fixInvoiceRate, RoundingMode.CEILING).multiply(rate).setScale(2, RoundingMode.HALF_UP));
                            policySettlementStatistics.setChannelAloneRate(rate);
                            policySettlementStatistics.setChannelFee(BigDecimal.ZERO);
                            policySettlementStatistics.setChannelFeeRate(BigDecimal.ZERO);
                            policySettlementStatistics.setChannelAloneName(cof.getChannel());
                            policySettlementStatistics.setOtherSettlementFalg(OtherSettlementFalg.Y.name());
                            return policySettlementStatistics;
                        }).collect(Collectors.toList());
                        //转换
                    } else {
                        insuranceStatistics = statisticsService.loadByInsuranceAreaAndPlatform(insurance, cf.getPlatform(), cof.getChannel(), date);
                        relations = insuranceStatistics.stream().map(static1 -> {
                            BigDecimal rate = cf.getFixRate();
                            PolicySettlementTypeStatistics policySettlementStatistics = policySettlementTypeStatisticsService.build(static1, info.getId(), RelationType.CHANNEL, rate);
                            policySettlementStatistics.setDate(lastMounthString);
                            policySettlementStatistics.setChannelFeeRate(rate);
                            policySettlementStatistics.setChannelFee(policySettlementStatistics.getExTaxPremium().divide(fixInvoiceRate, RoundingMode.CEILING).multiply(rate).setScale(2, RoundingMode.HALF_UP));
                            policySettlementStatistics.setOtherSettlementFalg(OtherSettlementFalg.N.name());
                            policySettlementStatistics.setChannelName(cof.getChannel());
                            policySettlementStatistics.setChannelAloneFee(BigDecimal.ZERO);
                            policySettlementStatistics.setChannelAloneRate(BigDecimal.ZERO);
                            return policySettlementStatistics;
                        }).collect(Collectors.toList());
                    }
                    addPolicy.addAll(relations);
                });
            });
            computerTotal(info, addPolicy);
            addSettlementInfo.add(info);
            addSettlementPolicyRelation.addAll(addPolicy);

        });
        transactionTemplate.execute(status -> {
            saveBatch(addSettlementInfo);
            List<PolicySettlementTypeStatistics> addSta = new ArrayList<>();
            addSettlementPolicyRelation.forEach(relea -> {
                PolicySettlementTypeStatistics fromDB = policySettlementTypeStatisticsService.queryByPolicyNo(relea.getPolicyNo(), RelationType.INSURANCE.name());
                if (fromDB == null) {
                    addSta.add(relea);
                } else {
                    relea.setInsuranceFee(fromDB.getInsuranceFee());
                    relea.setInsuranceFeeRate(fromDB.getInsuranceFeeRate());
                    relea.setInsuranceSettlementId(fromDB.getInsuranceSettlementId());
                    relea.setType(RelationType.CHANNEL.name());
                    addSta.add(relea);
                }
            });
            if (CollectionUtils.isNotEmpty(addSta)) {
                policySettlementTypeStatisticsService.saveBatch(addSta);
            }
            return null;
        });
    }

    @Override
    public IPage<ChannelSettlementInfo> queryChannelSettlementInfo(IPage<ChannelSettlementInfo> page, String date) {
        return getBaseMapper().queryChannelSettlementInfo(page, date);
    }

    @Override
    public IPage<ChannelSettlementInfo> queryChannelUnSettlementInfo(IPage<ChannelSettlementInfo> page, String date) {
        return getBaseMapper().queryChannelUnSettlementInfo(page, date);
    }

    @Override
    public void confirmPolicy(String id) {
        ChannelSettlementInfo settlementInfo = getById(id);
        Assert.isTrue(settlementInfo.getState().equals(SettlementState.CHECKING), "当前保单状态不是待确定状态,无法确定");
        settlementInfo.setState(SettlementState.CHECKED);
        updateById(settlementInfo);
    }

    @Override
    public void loadSettlementByUpLoadFile(String id, List<String> relations) {
        ChannelSettlementInfo info = getById(id);
        Assert.notNull("统计数据不存在");
        String channel = info.getChannel();
        ChannelSettlementConfig config = configService.queryByChannelName(channel);
        Assert.notNull(config, "{}渠道数据不存在或已经被删除", channel);
        BigDecimal invoiceRate = config.getInvoiceRate();
        BigDecimal fixInvoiceRate = BigDecimal.ONE.add(invoiceRate);
        config.buildConfigBean();
        if (CollectionUtils.isEmpty(relations)) {
            //清理数据
            List<PolicySettlementTypeStatistics> list = new ArrayList<>();
            transactionTemplate.execute(status -> {
                policySettlementTypeStatisticsMapper.deleteChannelSettlementInfo(id);
                policySettlementTypeStatisticsMapper.deleteChannelOtherSettlementInfo(id, relations);
                computerTotal(info, list);
                updateById(info);
                return null;
            });
            return;
        }
        //查询配置
        List<InsuranceStatistics> formDb = insuranceStatisticsService.loadByPolicyNo(relations);
        List<String> fromDBPolicyNos = formDb.stream().map(InsuranceStatistics::getPolicyNo).collect(Collectors.toList());
        List<String> uploadNos = new ArrayList<>(relations);
        uploadNos.removeAll(fromDBPolicyNos);
        Assert.isTrue(uploadNos.size() == 0, "上传保单的保单号{}不存在,", StringUtils.collectionToCommaDelimitedString(uploadNos));
        List<PolicySettlementTypeStatistics> relationList = formDb.stream().filter(statistics -> Arrays.asList(StatisticsPolicyStateEnum.CBZ.name(), StatisticsPolicyStateEnum.YCB.name()).contains(statistics.getPolicyState()))
                .map(statistics -> {
                    ChannelSettlementConfig.Config find = findConfig(statistics, config.getConfigs());
                    Assert.notNull("保单号{}未找到对应的配置区域", statistics.getPolicyNo());
                    BigDecimal rate = find.getFixRate();
                    Assert.notNull(rate, "{}未配置{}费率", config.getChannel(), "固定");
                    PolicySettlementTypeStatistics policySettlementStatistics = policySettlementTypeStatisticsService.build(statistics, info.getId(), RelationType.CHANNEL, rate);
                    policySettlementStatistics.setDate(info.getDate());
                    policySettlementStatistics.setChannelFeeRate(rate);
                    policySettlementStatistics.setOtherSettlementFalg(OtherSettlementFalg.N.name());
                    policySettlementStatistics.setChannelFee(policySettlementStatistics.getExTaxPremium().divide(fixInvoiceRate, RoundingMode.CEILING).multiply(rate).setScale(2, RoundingMode.HALF_UP));
                    policySettlementStatistics.setChannelAloneFee(BigDecimal.ZERO);
                    policySettlementStatistics.setChannelAloneRate(BigDecimal.ZERO);
                    policySettlementStatistics.setChannelName(channel);
                    return policySettlementStatistics;
                    //查找配置信息
                }).collect(Collectors.toList());

        transactionTemplate.execute(status -> {
            //清理历史数据
            policySettlementTypeStatisticsService.process(relationList, id, info.getDate(), RelationType.CHANNEL);
            policySettlementTypeStatisticsMapper.deleteChannelOtherSettlementInfo(id, relations);
            //查询数据列表
            List<PolicySettlementTypeStatistics> list = policySettlementTypeStatisticsService.lambdaQuery().eq(PolicySettlementTypeStatistics::getChannelSettlementId, id).list();
            computerTotal(info, list);
            updateById(info);
            return null;
        });
    }


    private ChannelSettlementConfig.Config findConfig(InsuranceStatistics statistics, List<ChannelSettlementConfig.Config> configs) {
        Optional<ChannelSettlementConfig.Config> find = configs.stream().filter(config -> {
            if (config.getInsuranceCompany().equals(statistics.getInsuranceCompany())) {
                if (config.getPlatform().contains(statistics.getPlatformCode())) {
                    return true;
                }
            }
            return false;
        }).findFirst();
        Assert.isTrue(find.isPresent(), "保单{},渠道{},保司{},地区{},平台{}未找到对应的渠道费率配置信息", statistics.getPolicyNo(), statistics.getChannelProvider(), statistics.getInsuranceCompany(), statistics.getArea(), statistics.getPlatformCode());
        return find.get();
    }

    private void computerTotal(ChannelSettlementInfo settlementInfo, List<PolicySettlementTypeStatistics> relations) {
        settlementInfo.setInsuranceNum(relations.size());
        AtomicReference<BigDecimal> insuranceAmt = new AtomicReference<>(BigDecimal.ZERO);
        AtomicReference<BigDecimal> benefit = new AtomicReference<>(BigDecimal.ZERO);
        relations.forEach(relation -> {
            BigDecimal amt = insuranceAmt.get().add(relation.getCost());
            BigDecimal bft = benefit.get().add(relation.getChannelFee()).add(relation.getChannelAloneFee());
            insuranceAmt.set(amt);
            benefit.set(bft);
        });
        settlementInfo.setState(SettlementState.CHECKING);
        settlementInfo.setInsuranceAmt(insuranceAmt.get());
        settlementInfo.setBenefit(benefit.get());
    }
}
